

#include "dbarch.h"
#include "dbtime.h"
#include "exceptions.h"


DBArch::DBArch()
{
	SetArchPath ( "arch_x" );
	tDiff = 0;
	tDiffExt = 0;
}

DBArch::~DBArch()
{
	LOGP1 ( "Archive: Deamon is closing..." );

	for ( u32 c = 0; c < cache_read.size(); c++ )
	{
		closeArchive ( cache_read[c] );
	}
}

string DBArch::timeToFName ( time_t sys_date )
{
	string fname;
	db_specTime st;
	DBTime dbt;
	dbt.FromSystemTime ( sys_date );
	st = dbt.translate();
	char buff[32];
	DBTime td;
	td.FromSystemTime ( sys_date );
	fname.append ( "DB_" );
	sprintf ( buff, "%d", st.year );
	fname.append ( buff );

	if ( st.month < 10 ) {
		fname.append ( "-0" );
	}
	else {
		fname.append ( "-" );
	}

	sprintf ( buff, "%d", st.month );
	fname.append ( buff );

	if ( st.day < 10 ) {
		fname.append ( "-0" );
	}
	else {
		fname.append ( "-" );
	}

	sprintf ( buff, "%d", st.day );
	fname.append ( buff );
	fname.append ( ".dba" );
	//LOGP2("DATE %s is",fname.c_str());
	return fname;
}

i32 DBArch::createNewFile ( time_t sys_time, time_t ext_time )
{
	string fname = arch_path;

	if ( ( fname[fname.size() - 1] != '\\' ) && ( fname[fname.size() - 1] != '/' ) );

	fname.push_back ( '/' );
	fname.append ( timeToFName ( sys_time ) );
	FILE *f = fopen ( fname.c_str(), "rb" );

	if ( f )
	{
		EXCEX ( 0xFF03, "Archive:File allready exist !" );
	}

	f = fopen ( fname.c_str(), "w+b" );

	if ( !f )
	{
		EXCEX ( 0xFF01, "Can't create file" );
	}

	dbf_header h;
	h.arch_blocks = 0;
	h.arch_creat_time = sys_time;
	h.arch_creat_time_ext = ext_time;
	h.checkbits = 0x4D7A;
	h.first_bat_addres = sizeof ( dbf_header );
	h.flags = 0;
	h.arch_bats = 1;
	h.end_addr = sizeof ( dbf_header ) + sizeof ( dbf_bat );    /// initial end of file
	h.crc_checksum = h.crc();
	//
	dbf_bat bat;
	bat.bat_nmbr = 0;
	bat.used_blocks = 0;
	bat.bat_addr = sizeof ( dbf_header );
	bat.indx_next = -1;
	bat.indx_prev = -1;
	i32 ret = fwrite ( ( void * ) &h, sizeof ( dbf_header ), 1, f );

	if ( ret != 1 ) {
		EXCEX ( 0xFF02, "Can't write to archive(header)" );
	}

	ret = fwrite ( ( void * ) &bat, sizeof ( dbf_bat ), 1, f );

	if ( ret != 1 ) {
		EXCEX ( 0xFF02, "Can't write to archive(bat)" );
	}

	LOGP2 ( "Archive: file [%s] created.", fname.c_str() );
	DBTime dtime;
	dtime.FromSystemTime ( sys_time );
	dtime.FillTime ( 0, 0, 0, 0 );
	DBIdent *ident = new DBIdent;
	ident->f = f;
	ident->flags = 0;
	ident->last_read = sysClock();
	ident->sys_date = dtime.dt.time;       // erase hours & minuts from time
	ident->writable = 1;
	ident->head = h;
	ident->bats.push_back ( bat );
	arch_now = ident;
	fflush ( f );
	time_t t1, t2;
	t1 = sysClock() / DAY * DAY;
	t2 = sys_time / DAY * DAY;

	if  ( t1 == t2 ) {
		ident->current = 1;
	}
	else {
		ident->current = 0;
	}

	pushToStack ( ident );
	return 1;
}

i32 DBArch::removeIdent ( DBIdent *ident )
{
	for ( u32 c = 0; c < cache_read.size(); c++ )
	{
		if ( cache_read[c] == ident )
		{
			if ( ident->f ) {
				closeArchive ( ident );
			}

			cache_read.erase ( cache_read.begin() + c );
			delete ident;
			return 1;
		}
	}

	return 0;
}

i32 DBArch::pushToStack ( DBIdent *ident )
{
	cache_read.push_back ( ident );
	int oldest = 0xFF;
	time_t old_time = sysClock();

	for ( u32 c = 0; c < cache_read.size(); c++ )
	{
		if ( cache_read[c]->last_read <= old_time )
		{
			if ( oldest != 0xFF )
				if ( cache_read[c]->last_read == cache_read[oldest]->last_read )
				{
					continue;       /// Prefer elements from beginig
				}

			old_time = cache_read[c]->last_read;
			oldest = c;
		}
	}

////////	LOGP4 ( "REMOVEME: siz, old tm, now - max %d %d %d", cache_read.size(), old_time, sysClock() - MAX_FILE_OLD );

	if ( ( cache_read.size() < HARD_FILE_LIMIT ) && ( old_time > sysClock() - MAX_FILE_OLD ) ) {
		return 1;
	}

	closeArchive ( cache_read[oldest] );

	if ( cache_read.size() < HARD_FILE_LIMIT )
	{
		LOGP1 ( "Archive: File closed because MAX_FILE_OLD is exceeded." );
	}
	else
	{
		LOGP1 ( "Archive: File closed because HARD_FILE_LIMIT is exceeded." );
	}

	cache_read.erase ( cache_read.begin() + oldest );

	if ( old_time < sysClock() - MAX_FILE_OLD ) {
		removeOldStack();    /// check if theres more of this stuff
	}

	return 1;
}

i32 DBArch::removeOldStack()
{
	vector<DBIdent *>::iterator i;
	DBIdent *ident;

	for ( i = cache_read.begin(); i != cache_read.end(); i++ )
	{
		ident = *i;

		if ( ( ident->last_read < sysClock() - MAX_FILE_OLD ) && !ident->current ) {
			closeArchive ( ident );
			i = cache_read.erase ( i );
			LOGP1 ( "Archive: Closed file [MAX_RETENTION_TIME exceed.]" );
		}
	}

	return 1;
}

i32 DBArch::closeArchive ( DBIdent *ident )
{
	if ( ident->f )
	{
		fclose ( ident->f );
		ident->f = 0;
	}

	string file = timeToFName ( ident->sys_date );
	LOGP2 ( "Archive: Closed file [%s]", file.c_str() );
	return 1;
}

i32 DBArch::openArchive ( time_t sys_date, i32 writable )
{
	string fname = arch_path;

	if ( ( fname[fname.size() - 1] != '\\' ) && ( fname[fname.size() - 1] != '/' ) )
	{
		fname.push_back ( '/' );
	}

	fname.append ( timeToFName ( sys_date ) );
	DBIdent *ident = 0;
	FILE *file;

	if ( !writable )
	{
		file = fopen ( fname.c_str(), "rb" );
	}
	else
	{
		file = fopen ( fname.c_str(), "rb+" );
	}

	if ( !file && !writable )
	{
		LOGP2 ( "Archive: File [%s] don't exist.", fname.c_str() );
		return 0;
	}

	if ( !file && writable )
	{
		try
		{
			createNewFile ( sys_date, extClock() );
		}
		catch ( cexception )
		{
			LOGP2 ( "ARCHIVE: FAILED TO CREATE NEW FILE [%s] !!! Fatal eror.", fname.c_str() );
			return 0;
		}

		return 1;
	}

	DBTime dtime;
	dtime.FromSystemTime ( sys_date );
	dtime.FillTime ( 0, 0, 0, 0 );
	u32 size;
	ident = new DBIdent;
	ident->f = file;
	ident->sys_date = dtime.dt.time;
	ident->last_read = sysClock();
	ident->flags = 0;
	ident->writable = writable;
	time_t t1, t2;
	t1 = sysClock() / DAY * DAY;
	t2 = sys_date / DAY * DAY;
	pushToStack ( ident );

	if  ( t1 == t2 ) {
		setCurrent ( ident );
	}
	else {
		ident->current = 0;
	}

	if ( !llsize ( ident, size ) )
	{
		EXCEX ( 0xAAE2, "E03: Can't get file size. Archive loading aborted." );
	}

	if ( size / 1024 < 1 ) {
		size = 1024;
	}

	LOGP3 ( "Archive: [%s] opened [%d kB].", fname.c_str(), ( int ) ( size / 1024 ) );

	try     // try to read file structure
	{
		llread ( ident, ( void * ) &ident->head, sizeof ( dbf_header ), 0 );
		u32 calc = ident->head.crc();
		DBTime dbt;
		dbt.FromSystemTime ( ident->head.arch_creat_time );
		u32 size;

		if ( !llsize ( ident, size ) ) {
			EXCEX ( 0xAAE2, "E03: Can't get file size. Archive loading aborted." );
		}

		LOGP5 ( "Archive: Created[%s],CRC[%X];BAT/BLOCK[%d/%d]", dbt.DateToString().c_str(), ident->head.crc_checksum,
				ident->head.arch_bats, ident->head.arch_blocks );

		if ( ident->head.checkbits != 0x4D7A )
		{
			EXCEX ( 0xAAE1, "E01:Header checkbytes error. Archive loading aborted." );
		}

		if ( calc != ident->head.crc_checksum )
		{
			EXCEX ( 0xAAE0, "E02:Header CRC mismatched. Archive loading aborted." );
		}

		// check file size:
		if ( size != ident->head.end_addr ) {
			EXCEX ( 0xAAE3, "E04: Bad end_addr. Archive loading aborted." );
		}

		u32 offset = ident->head.first_bat_addres;
		dbf_bat bat;

		if ( offset >= size ) {
			EXCEX ( 0xAAE3, "E05: Bad jump to BAT. Archive loading aborted." );
		}

		for ( u32 c = 0; c < ident->head.arch_bats; c++ )
		{
			llread ( ident, &bat, sizeof ( dbf_bat ), offset );
			offset = bat.indx_next;
			ident->bats.push_back ( bat );
			LOGP3 ( "DIAGNOSTIC:BAT num[%d] %X", bat.bat_nmbr, bat.bat_crc );
		}
	}
	catch ( cexception )
	{
		LOGP2 ( "ARCHIVE: EXCEPTION catched in %s", __FUNCTION__ );
		removeIdent ( ident );
		return 0;
	}

	i32 ret = validateArch ( ident );

	if ( !ret )
	{
		LOGP1 ( "ARCHIVE: Validation failed. Archive not usable." );
		removeIdent ( ident );
	}

	return 1;
}

i32 DBArch::llsize ( DBIdent *ident, u32 &size )
{
	i32 ret = fseek ( ident->f, 0, SEEK_END );

	if ( ret ) {
		return 0;
	}

	size = ftell ( ident->f );
	return 1;
}

i32 DBArch::llread ( DBIdent *ident, void *data, i32 size, i32 fpointer )
{
	i32 ret;
//	LOGP3 ( "DIAGNOSTIC read [%d %d]", size, fpointer );
	ident->last_read = sysClock();

	if ( fpointer != -1 )
	{
		ret = fseek ( ident->f, fpointer, SEEK_SET );

		if ( ret ) // error
		{
			EXCEX ( 0xFFA1, "Can't seek" );
		}
	}

//	LOGP4 ( "tell %d %d %d", ftell ( ident->f ),size,fpointer );
//	char buff[4096];
	ret = fread ( data, size, 1, ident->f );
//	memcpy ( data, buff, size );
//	LOGP4 ( "tell %d %d %d", ftell ( ident->f ),size,fpointer );

	if ( ret != 1 )
	{
		LOGP4 ( "Errors %d %d %d", ferror ( ident->f ), feof ( ident->f ) , ftell ( ident->f ) );
		EXCEX ( 0xFFA2, "Can't read" );
	}

	return 1;
}

i32 DBArch::llwrite ( DBIdent *ident, void *ptr, i32 size, i32 fpointer )
{
//	LOGP3 ( "DIAGNOSTIC write [%d %d]", size, fpointer );
	i32 ret;
	ident->last_read = sysClock();
	ret = fseek ( ident->f, fpointer, SEEK_SET );

	if ( ret ) // error
	{
		EXCEX ( 0xFFA1, "Can't seek" );
	}

	ret = fwrite ( ptr, size, 1, ident->f );

	if ( ret != 1 )
	{
		EXCEX ( 0xFFA2, "Can't write" );
	}
	fflush(ident->f);

	return 1;
}


i32 DBArch::lookupIdent ( time_t sys_time, u32 &cache )
{
	DBTime ti;
	ti.FromSystemTime ( sys_time );
	ti.FillTime ( 0, 0, 0, 0 );
	sys_time = ti.dt.time;
//	LOGP2 ( "*********** %s", ctime ( &sys_time ) );

	for ( u32 c = 0; c < cache_read.size(); c++ )
	{
//		LOGP2 ( "syst %s", ctime ( &sys_time ) );
//		LOGP2 ( "lookup %s", ctime ( &cache_read[c]->sys_date ) );

		if ( cache_read[c]->sys_date == sys_time )
		{
			cache = c;
			cache_read[c]->last_read = sysClock();
			return 1;
		}
	}

//	LOGP1 ( "LOOKUP FAAILED" );
	return 0;           /// Lookup failed
}

i32 DBArch::allocateBlock ( time_t sys_time, time_t ext_time, i32 size, u8 type, dbf_block **block_ptr, dbf_bat **batptr )
{
	if ( size > BLOCK_MAX_SIZE ) {
		EXCEX ( 0xFF06, "Size more than BLOCK_MAX_SIZE is not allowed" );
	}

	u32 cache, ret;
	ret = lookupIdent ( sys_time, cache );

	if ( !ret )
	{
		if ( !openArchive ( sys_time, 1 ) )
		{
			EXCEX ( 0xFF04, "Archive:Failed to allocate block" );
			return 0;
		}
	}

	ret = lookupIdent ( sys_time, cache );

	if ( !ret ) {
		EXCEX ( 0xFF05, "Archive:Internal error - fatal" );
	}

	DBIdent *ident = cache_read[cache];

	if ( !ident->writable )
	{
		LOGP1 ( "Archive: Can't allocate block, because archive is not writable !" );
		return 0;
	}

	dbf_bat *bat = &ident->bats[ident->bats.size() - 1];
	dbf_block *block;

	if ( bat->used_blocks >= SYS_BAT_COUNT ) {
		addBat ( ident );
	}

	bat = &ident->bats[ident->bats.size() - 1];

	if ( bat->used_blocks < SYS_BAT_COUNT )
	{
		block = &bat->block[bat->used_blocks];
		block->bl_addr = ident->head.end_addr;
		ident->head.end_addr += size;
		block->bl_size = size;
		block->bl_time = sys_time;
		block->bl_time_ext = ext_time;
		block->bl_type = 0xFF;                  /// set default. Change type, when you actualy write data to archive
		block->nmbr = bat->used_blocks;
		bat->used_blocks ++;
		ident->head.arch_blocks++;
		writeHeader ( ident );
		static char null[BLOCK_MAX_SIZE] ;          // zero block data - needed ? -> YES !!!
		memset ( null, 0, BLOCK_MAX_SIZE );
		null[0] = '*';
		block->data_crc = dataCRC ( null, size );
		block->bl_crc = block->crc();
		bat->bat_crc = bat->crc();
		*batptr = bat;
		llwrite ( ident, &null, size, block->bl_addr );
		llwrite ( ident, ( void * ) bat, sizeof ( dbf_bat ), bat->bat_addr );
	}

	if ( block_ptr )
	{
		*block_ptr = block;
	}

	return 1;
}

i32 DBArch::addBat ( DBIdent *ident )
{
	dbf_bat bat;
	dbf_bat *prev = &ident->bats[ident->bats.size() - 1];
	bat.bat_nmbr = prev->bat_nmbr + 1;
	bat.bat_addr = ident->head.end_addr;
	ident->head.end_addr += sizeof ( dbf_bat );
	ident->head.arch_bats ++;
	bat.indx_prev = prev->bat_addr;
	prev->indx_next = bat.bat_addr;
	prev->bat_crc = prev->crc();
	bat.indx_next = -1;
	bat.used_blocks = 0;
	bat.bat_crc = bat.crc();
	llwrite ( ident, &bat, sizeof ( dbf_bat ), bat.bat_addr );
	llwrite ( ident, prev, sizeof ( dbf_bat ), prev->bat_addr );
//	llwrite ( ident, &ident->head, sizeof ( dbf_header ), 0 );
	writeHeader ( ident );
	ident->bats.push_back ( bat );
	return 1;
}

i32 DBArch::writeBlock ( time_t sys_time, time_t ext_time, u8 type, u32 size, void *data )
{
	i32 ret;
	dbf_block *block;
	dbf_bat *bat;

	try
	{
		ret = allocateBlock ( sys_time, ext_time, size, type, &block, &bat );

		if ( !ret )
		{
			LOGP3 ( "Archive: Can't write block [%d/%d] (allocation error)", type, size );
			EXCC ( 0 );
		}

		u32 cache = 0;
		ret = lookupIdent ( sys_time, cache );

		if ( !ret )
		{
			LOGP3 ( "Archive: Can't write block [%d/%d]. Archive not matched !!! [FATAL] ", type, size );
			EXCEX ( 0xFFFFFFFF, "Serious internal error. System halted." );
		}

		DBIdent *ident = cache_read[cache];
		llwrite ( ident, data, size, block->bl_addr );
//		LOGP4 ( "DIAGNOSTIC: Archive: block [%d->%d/%d] saved", block->nmbr, block->bl_type, block->bl_size );
		block->data_crc = dataCRC ( data, size );
		block->bl_type = type;                  /// data saved, so change type from default 0xFF to actual type
		block->bl_crc = block->crc();
		bat->bat_crc = bat->crc();
		llwrite ( ident, ( void * ) bat, sizeof ( dbf_bat ), bat->bat_addr );   // update bat with changed block
	}
	catch ( cexception )
	{
		LOGP4 ( "ARCHIVE: Parachute used on %s, %d/%d", ctime ( &sys_time ), type, size );
		DBParachute::PARA()->WriteBlock ( sys_time + DAY * 4, ext_time, type, data, size, "??? EXCEPTION when writing block" );
	}

	return 1;
}

i32 DBArch::readBlock ( dbf_search_result result, void *data )
{
	DBIdent *ident = ( DBIdent * ) result.ident;

	try
	{
		llread ( ident, data, result.block_size, result.block_addr );
	}
	catch ( cexception )
	{
		LOGP4 ( "Archive: Failed to read block [bt %d/bl %d/sz %d", result.bat_number, result.block_count, result.block_size );
		return 0;
	}

	return 1;
}

i32 DBArch::writeHeader ( DBIdent *ident )
{
	ident->head.crc_checksum = ident->head.crc();

	try
	{
		llwrite ( ident, &ident->head, sizeof ( dbf_header ), 0 );
	}
	catch ( cexception )
	{
		LOGP2 ( "EXC : ", __FUNCTION__ );
	}

	return 1;
}

i32 DBArch::searchBlocks ( time_t time_from, time_t time_to, u8 type, u32 flags, vector<dbf_search_result> &results )
{
	i32 fromto = 0;
	i32 mtype = 0;
	i32 ext = 0;

	if ( ( flags & SRCH_FROMTO ) == SRCH_FROMTO ) {
		fromto = 1;
	}

	if ( ( flags & SRCH_TYPEMATCH ) == SRCH_TYPEMATCH ) {
		mtype = 1;
	}

	if ( ( flags & SRCH_EXTTIME ) == SRCH_EXTTIME ) {
		ext = 1;
	}

	DBIdent *ident;
	u32 cache;

	if ( !lookupIdent ( time_from, cache ) )
	{
		time_t tnow = sysClock() / DAY * DAY;
		time_t tsys = time_from / DAY * DAY;
		int writable = 0;

		if ( tsys == tnow ) {
			writable = 1;
		}

		openArchive ( time_from, writable );
	}

	if ( !lookupIdent ( time_from, cache ) )
	{
		LOGP2 ( "Archive: Can't find Ident [%s]", ctime ( &time_from ) );
		return 0;
	}

	ident = cache_read[cache];
	dbf_block *block;
	dbf_search_result rs;
	time_t tval;
	i32 count = 0;

	for ( u32 c = 0; c < ident->bats.size(); c++ )
	{
		for ( i32 v = 0; v < ident->bats[c].used_blocks; v++ )
		{
			block = &ident->bats[c].block[v];

			if ( ext ) {
				tval = block->bl_time_ext;
			}
			else {
				tval = block->bl_time;
			}

			if ( fromto )
			{
				if ( tval < time_from || tval > time_to )
				{
					continue;
				}
			}
			else
			{
				if ( tval != time_from ) {
					continue;
				}
			}

			if ( mtype )
			{
				if ( block->bl_type != type ) {
					continue;
				}
			}

			rs.bat_addr = ident->bats[c].bat_nmbr;
			rs.bat_number = c;
			rs.block_count = v;
			rs.block_size = block->bl_size;
			rs.block_type = block->bl_type;
			rs.block_addr = block->bl_addr;
			rs.extT = block->bl_time_ext;
			rs.sysT = block->bl_time;
			rs.result_number = count;
			rs.ident = ident;
			count ++;
			results.push_back ( rs );
		}
	}

	LOGP2 ( "DIAGNOSTIC : Found %d results...", count );
	return count;
}

i32 DBArch::search ( time_t time_from, time_t time_to, u8 type, u32 flags, vector<dbf_search_result> &results )
{
	DBTime f, t;
	f.FromSystemTime(time_from);
	t.FromSystemTime(time_to);
	f.FillTime(0,0,0,0);
	t.FillTime(0,0,0,0);

	   LOGP3("Searching from... %s - %s",DS_TD(time_from),DS_TD(time_to));

	if ( f.dt.time == t.dt.time || ( ( flags & SRCH_MATCH ) == SRCH_MATCH ) )   // search in only one date;
	{
		return searchBlocks ( time_from, time_to, type, flags, results );
	}

	time_t tdiff = t.dt.time - f.dt.time;
//	u32 days = tdiff / DAY  + 1;
	u32 days = tdiff / DAY ;
	LOGP2 ( "SEARCH DAYS %d", days );

		DBTime dbt;
	for ( u32 c = 0; c < days; c++ )
	{
		time_t tstart = f.dt.time + DAY * c;
		dbt.FromSystemTime(tstart);
		dbt.FillTime(0,0,0,0);
		tstart = dbt.ToSystemTime();


		dbt.FromSystemTime ( tstart );
		dbt.FillTime(23,59,59,999);
		time_t tend = dbt.ToSystemTime();       // same date, but the end of the day.
		i32 ret = isSummerTime ( tend );                      /// if ret < 0 then no shift !!!

		if ( ret == 1 ) {
	//                      tend += g_tShiftS;
		}

		if ( ret == 0 ) {
	//                      tend += g_tShiftW;
		}

		if ( tstart < time_from ) {
			tstart = time_from;
		}

		if ( tend > time_to ) {
			tend = time_to;
		}

		flags |= SRCH_FROMTO;
		DBTime t1, t2;
		t1.FromSystemTime ( tstart );
		t2.FromSystemTime ( tend );
		LOGP3 ( "Searching from %s to %s ", t1.TimeDateToString().c_str(), t2.TimeDateToString().c_str() );
	//              searchBlocks ( tstart, tend,type,flags,results );
	}

	return 1;
}

i32 DBArch::setCurrent ( DBIdent *ident )
{
	for ( u32 c = 0; c < cache_read.size(); c++ )
	{
		cache_read[c]->current = 0;
	}

	ident->current = 1;
	return 1;
}

u32 DBArch::isSummerTime ( time_t sys_time )
{
	tm *timeinfo = localtime ( &sys_time );

	if ( !timeinfo )
	{
		EXCEX ( 0xB000, "localtime returned zero !!!" );
	}

	//LOGP2("Saving time %d",timeinfo->tm_isdst);
	if ( timeinfo->tm_isdst < 0 ) {
		return -1;
	}

	return timeinfo->tm_isdst;
}

i32 DBArch::validateArch ( DBIdent *ident )
{
	dbf_bat *bat;
	dbf_block *block;
	char *vbuff = new char[DEFAULT_VALIDATION_BUFFER];
	u32 vbsize = DEFAULT_VALIDATION_BUFFER;

	try
	{
		if ( ident->head.arch_bats != ident->bats.size() )
		{
			LOGP3 ( "VALIDATE ERROR# BAT COUNT %d/%d", ident->head.arch_bats, ident->bats.size() );
			EXCC ( 0 );
		}

		u32 val;

		for ( u32 c = 0; c < ident->bats.size(); c++ )
		{
			bat = &ident->bats[c];
			val = bat->crc();

			if ( bat->bat_crc != val )
			{
				LOGP2 ( "VALIDATE ERROR# BAT CRC at %d", bat->bat_nmbr );
				EXCC ( 0 );
			}

			for ( i32 v = 0; v < bat->used_blocks; v++ )
			{
				block = &ident->bats[c].block[v];
				val = block->crc();

				if ( block->bl_crc != val )
				{
					LOGP3 ( "VALIDATE ERROR# BLOCK CRC at %d.%d", c, v );
					EXCC ( 0 );
				}

				if ( block->bl_size > vbsize )  /// resize buffer
				{
					delete[] vbuff;
					vbuff = new char[block->bl_size];
					vbsize = block->bl_size;
				}

				llread ( ident, vbuff, block->bl_size, block->bl_addr );
				val = dataCRC ( vbuff, block->bl_size );

				if ( val != block->data_crc )
				{
					LOGP4 ( "VALIDATE ERROR# Block data CRC not matched %d/%d [size %d]", c, v, block->bl_size );
					EXCC ( 0 );
				}
			}
		}
	}
	catch ( cexception )
	{
		delete[] vbuff;
		return 0;
	}

	return 1;
}
